#pragma once

#include <AVUIControl.h>

namespace AVUI {

class Panel;
class ScrollViewer;
class DataTemplate;
class DataTemplateSelector;
class StyleSelector;
class ItemContainerGenerator;
class ItemCollection;
class CollectionChangedEventArgs;
class Keyboard;
class InputDevice;
class KeyboardFocusChangedEventArgs;

class ItemsPanelTemplate;

class ItemNavigateArgs
{
public:

    ItemNavigateArgs(InputDevice* pDeviceUsed, ModifierKeys::Enum modifierKeys) : m_pDeviceUsed(pDeviceUsed), m_modifierKeys(modifierKeys) { }

    InputDevice* get_DeviceUsed() { return m_pDeviceUsed; }

    static ItemNavigateArgs* get_Empty()
    {
        static ItemNavigateArgs empty(NULL, ModifierKeys::None);

        return &empty;
    }

private:

    InputDevice* m_pDeviceUsed;
    ModifierKeys::Enum m_modifierKeys;
};

class ItemsControl : public Control
{
    friend class Panel;
    friend class ComboBox;
    friend class HeaderedItemsControl;
    friend class MenuItem;
    friend class Menu;
    friend class ListBox;
public:

    ItemsControl() : m_pFocusedItem(NULL), m_isScrollHostValid(false) { }
    virtual ~ItemsControl() { }

    DECLARE_ELEMENT(ItemsControl, Control);

    DECLARE_DEPENDENCY_PROPERTY(ItemsSource);
    TRefCountedPtr<String> get_ItemsSource() { return (String*)(Object*)GetValue(get_ItemsSourceProperty()); }
    void set_ItemsSource(String* pValue) { SetValue(get_ItemsSourceProperty(), (Object*)pValue); } 

    DECLARE_DEPENDENCY_PROPERTY(ItemTemplate);
    TRefCountedPtr<DataTemplate> get_ItemTemplate() { return (DataTemplate*)(Object*)GetValue(get_ItemTemplateProperty()); }
    void set_ItemTemplate(DataTemplate* pValue) { SetValue(get_ItemTemplateProperty(), (Object*)pValue); } 

    DECLARE_DEPENDENCY_PROPERTY(ItemTemplateSelector);
    TRefCountedPtr<DataTemplateSelector> get_ItemTemplateSelector() { return (DataTemplateSelector*)(Object*)GetValue(get_ItemTemplateSelectorProperty()); }
    void set_ItemTemplateSelector(DataTemplateSelector* pValue) { SetValue(get_ItemTemplateSelectorProperty(), (Object*)pValue); } 

    DECLARE_DEPENDENCY_PROPERTY(ItemContainerStyle);
    TRefCountedPtr<Style> get_ItemContainerStyle() { return (Style*)(Object*)GetValue(get_ItemContainerStyleProperty()); }
    void set_ItemContainerStyle(Style* pValue) { SetValue(get_ItemContainerStyleProperty(), (Object*)pValue); } 

    DECLARE_DEPENDENCY_PROPERTY(ItemContainerStyleSelector);
    TRefCountedPtr<StyleSelector> get_ItemContainerStyleSelector() { return (StyleSelector*)(Object*)GetValue(get_ItemContainerStyleSelectorProperty()); }
    void set_ItemContainerStyleSelector(StyleSelector* pValue) { SetValue(get_ItemContainerStyleSelectorProperty(), (Object*)pValue); } 

    DECLARE_DEPENDENCY_PROPERTY(ItemsPanel);
    TRefCountedPtr<ItemsPanelTemplate> get_ItemsPanel() { return (ItemsPanelTemplate*)(Object*)GetValue(get_ItemsPanelProperty()); }
    void set_ItemsPanel(ItemsPanelTemplate* pValue) { SetValue(get_ItemsPanelProperty(), (Object*)pValue); } 

    DECLARE_READONLY_DEPENDENCY_PROPERTY(HasItems)
    bool get_HasItems() { return AVUI::UnboxValue<bool>(GetValue(get_HasItemsProperty())); }

    DECLARE_READONLY_DEPENDENCY_PROPERTY(IsGrouping)
    bool get_IsGrouping() { return AVUI::UnboxValue<bool>(GetValue(get_IsGroupingProperty())); }

    IMPLEMENT_READONLY_REFERENCE_PROPERTY(Items, ItemCollection, true);

    // GroupStyleSelector

    ItemContainerGenerator* get_ItemContainerGenerator()
    {
        if(m_pItemContainerGenerator == NULL)
        {
            CreateItemCollectionAndGenerator();
        }
        return m_pItemContainerGenerator;
    }

    ItemCollection* get_View() { return get_Items(); }

    void ClearContainerForItem(DependencyObject* pContainer, Object* pItem); 

 

    static ItemsControl* GetItemsOwner(DependencyObject* pElement);
    void PrepareItemContainer(DependencyObject* pContainer, Object* pItem);
    TRefCountedPtr<DependencyObject> GetContainerForItem(Object* pItem);

    ItemCollection* get_Items();
    static ItemsControl* ItemsControlFromItemContainer(DependencyObject* pContainer);
    bool IsHostForItemContainer(DependencyObject* pContainer);
    Object* GetItemOrContainerFromContainer(DependencyObject* pContainer);

    virtual void FocusItem(Object* pItem, ItemNavigateArgs* pItemNavigateArgs);

protected:
    virtual void ClearContainerForItemOverride(DependencyObject* pElement, Object* pItem) { }
    virtual bool ShouldApplyItemContainerStyle(DependencyObject* pContainer, Object* pItem) { return true; }

    virtual void OnItemsSourceChanged(Object* pOldValue, Object* pNewValue) { }
    virtual void PrepareContainerForItemOverride(DependencyObject* pElement, Object* pItem);
    virtual bool IsItemItsOwnContainerOverride(Object* pItem);
    virtual TRefCountedPtr<DependencyObject> GetContainerForItemOverride();
    virtual void OnItemsChanged(CollectionChangedEventArgs* pArgs) { };

    virtual void OnGotFocus(RoutedEventArgs* pArgs) { Control::OnGotFocus(pArgs); }

    void DoAutoScroll();
    void DoAutoScroll(Object* pStartingItem);

    void NavigateByPage(FocusNavigationDirection::Enum direction, ItemNavigateArgs* pItemNavigateArgs)
    {
        NavigateByPage(get_FocusedItem(), direction, pItemNavigateArgs);
    }

    void NavigateByPage(Object* pStartingItem, FocusNavigationDirection::Enum direction, ItemNavigateArgs* pItemNavigateArgs);
    void NavigateToEnd(ItemNavigateArgs* pItemNavigateArgs);
    void NavigateToStart(ItemNavigateArgs* pItemNavigateArgs);
    void NavigateByLine(FocusNavigationDirection::Enum direction, ItemNavigateArgs* pItemNavigateArgs);
    void NavigateByLine(Object* pStartingItem, FocusNavigationDirection::Enum direction, ItemNavigateArgs* pItemNavigateArgs);
    void NavigateByPageInternal(Object* pStartingItem, FocusNavigationDirection::Enum direction, ItemNavigateArgs* pItemNavigateArgs);

    void NavigateToItem(Object* pItem, ItemNavigateArgs* pItemNavigateArgs)
    {
        NavigateToItem(pItem, -1, pItemNavigateArgs, false);
    }
 
    void NavigateToItem(Object* pItem, int itemIndex, ItemNavigateArgs* pItemNavigateArgs)
    {
        NavigateToItem(pItem, itemIndex, pItemNavigateArgs, false);
    }

    void NavigateToItem(Object* pItem, ItemNavigateArgs* pItemNavigateArgs, bool alwaysAtTopOfViewport)
    {
        NavigateToItem(pItem, -1, pItemNavigateArgs, alwaysAtTopOfViewport);
    }


 

 

    Object* get_FocusedItem() { return m_pFocusedItem; }
    ScrollViewer* get_ScrollHost();


    // Deviation here -- This is to prevent directional keyboard navigation from getting 'stuck' on combobox
    bool get_IsItemsHostLogicalHorizontal();
    bool get_IsItemsHostLogicalVertical();

    TRefCountedPtr<Object> get_LogicalChildren();

private:
    static void StaticConstructor();

    bool get_IsInitPending() { return FrameworkElement::ReadInternalFlag(InternalFlags::InitPending); }
    bool get_IsLogicalVertical();
    bool get_IsLogicalHorizontal();

    Object* GetEncapsulatingItem(UIElement* pElement);
    Object* FindFocusable(int startIndex, int direction, int* pFoundIndex);
    void NavigateToItem(Object* pItem, int elementIndex, ItemNavigateArgs* pItemNavigateArgs, bool alwaysAtTopOfViewport);
    void NavigateByLineInternal(Object* pStartingItem, FocusNavigationDirection::Enum direction, ItemNavigateArgs* pItemNavigateArgs);

    void NavigateToFirstItemOnCurrentPage(Object* pStartingItem, FocusNavigationDirection::Enum direction, ItemNavigateArgs* pItemNavigateArgs);
    Object* GetFirstItemOnCurrentPage(Object* pStartingItem, FocusNavigationDirection::Enum direction, int* pFoundIndex);

 

    bool IsOnCurrentPage(Object* pItem, FocusNavigationDirection::Enum axis);
    bool IsOnCurrentPage(UIElement* pElement, FocusNavigationDirection::Enum axis);
    bool IsOnCurrentPage(UIElement* pElement, FocusNavigationDirection::Enum axis, bool fullyVisible);
    bool MakeVisible(int index)
    {
        return MakeVisible(index, false, false);
    }
    bool MakeVisible(int index, bool alwaysAtTopOfViewport, bool alignMinorAxisToo);

 

 


    void OnItemCollectionChanged(Object* pSender, CollectionChangedEventArgs* pArgs);
    void PrepareItemsControl(Object* pItem, DataTemplate* pItemTemplate, DataTemplateSelector* pItemTemplateSelector, Style* pItemContainerStyle, StyleSelector* pItemContainerStyleSelector);
    void ApplyItemContainerStyle(DependencyObject* pContainer, Object* pItem);
    static void OnItemsSourceChanged(DependencyObject* pDO, DependencyPropertyChangedEventArgs* e);
    static void OnGotFocus(Object* pSender, KeyboardFocusChangedEventArgs* e);
 

    Panel* get_ItemsHost() { return m_pItemsHost; }
    void set_ItemsHost(Panel* pPanel) { m_pItemsHost = pPanel; }

    void CreateItemCollectionAndGenerator();

    Object* m_pFocusedItem;
    bool m_isScrollHostValid : 1;
    TRefCountedPtr<Panel> m_pItemsHost; 
    TRefCountedPtr<ScrollViewer> m_pScrollHost; 
    TRefCountedPtr<ItemContainerGenerator> m_pItemContainerGenerator;
    TRefCountedPtr<ItemCollection> m_pItems; 
};


}; // namespace AVUI
